home *** CD-ROM | disk | FTP | other *** search
- /* *****************************************************************************
- FILE: GIF.c
-
- DESCRIPTION: routine for reading in GIF files
-
- ***************************************************************************** */
-
- #ifndef __COLOURLAB__
- #define __COLOURLAB__
- #endif
-
- #include "GIF.h"
- //#include "ColourLabWindows.h"
- //#include "FloatingWindows.h"
- //#include "WindowManagerMenu.h"
- //#include "ImagePalette.h"
- #include "Progress.h"
- #include "BitMapUtils.h"
-
- #include <LowMem.h>
-
- #ifndef __powerc
- #include <Errors.h>
- #endif
-
- //#define USE_PROGRESS_DIALOG
-
- short gMessage;
-
-
-
- #define ColorMapMask 0x80
- #define ImageSeparator 0x2C
- #define ExtensionIntroducer 0x21
- #define NullBlock 0x00
- #define InterlaceMask 0x40
- #define BufferSize 8192 // May be freely modified
-
- #define abort(x) { gMessage = x; goto end; }
- #define Check(x) if (e) abort(x) // this means return e
- #define Read(c, buf) count = c; \
- e = FSRead (refNum, &count, buf); /* Read the requested number of bytes */ \
- Check(strCantRead) \
- FileSize -= count /* And note that we have read them */
-
- #define NextByte(b) Read(1, &b)
-
- #define Skip(n) e = SetFPos (refNum, fsFromMark, n); /* Skip n bytes */ \
- Check(strCantSetFPos) \
- FileSize -= n // And note that we have read them */
-
- #define FillBuffer { \
- BufferPointer = BufferBase; \
- Read (BufferSize <= FileSize ? BufferSize : FileSize, BufferBase); \
- }
-
- #define BufferGetOne { \
- if (++BufferPointer >= BufferTop) /* If we are at the end of the buffer */ \
- FillBuffer /* Then fill it again */ \
- if (BlockCount == 0) { /* If the next byte is a length byte */ \
- BlockCount = *(BufferPointer++); /* Then skip it and update BlockCount */ \
- if (BufferPointer == BufferTop) /* If this brings us to the end of the buffer */ \
- FillBuffer /* Then fill it again */ \
- } \
- BlockCount--; \
- }
-
- #define ReadCode(code) { \
- if (BitOffset == 0) BufferGetOne \
- data = *BufferPointer; /* Read the current byte */ \
- newBitOffset = BitOffset + CodeSize; \
- if (newBitOffset > 8) { /* If we need more, read the next byte */ \
- BufferGetOne \
- data += ((long) (*BufferPointer)) << 8; \
- } \
- if (newBitOffset > 16) { /* If we still need more, read a third byte */ \
- BufferGetOne \
- data += ((long) (*BufferPointer)) << 16; \
- } \
- data >>= BitOffset; /* Skip the bits we already processed */ \
- BitOffset = newBitOffset & 7; /* Compute the new bit offset */ \
- code = data & ReadMask; /* Mask off the bits we don't want yet */ \
- }
-
- #ifdef USE_PROGRESS_DIALOG
- #define DrawPixel(index) { \
- * (Byte*) curAddr = index; /* Write out the pixel */ \
- curAddr = (long *) ((Byte*) curAddr + 1); /* Update the pointer */ \
- if (++xc == Width) { /* Update the x-coordinate */ \
- xc = 0; /* If it overflows, update the y-coordinate */ \
- if (!Interlaced) /* In a non-interlaced picture, just */ \
- yc++; /* increment yc to the next scan line */ \
- else { \
- switch (Pass) { /* Otherwise deal with the interlace as */ \
- case 0: /* described in the GIF spec */ \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 4; } \
- break; \
- case 1: \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 2; } \
- break; \
- case 2: \
- yc += 4; \
- if (yc >= Height) { Pass++; yc = 1; } \
- break; \
- case 3: \
- yc += 2; \
- break; \
- default: \
- break; \
- } \
- } \
- curAddr = (long*) ((long) srcBaseAddr + (long) yc * (long) rowBytes); /* update current address in bitmap */ \
- \
- /* $$GCOX- update progress bar*/\
- \
- SetGWorld(oldWorld,oldDevice);\
- if (! ProgressUpdate(progRefNum,yc)) {\
- e = userCanceledErr; \
- abort(e); }\
- SetGWorld(*world,NULL);\
- } \
- }
- #else
- #define DrawPixel(index) { \
- * (Byte*) curAddr = index; /* Write out the pixel */ \
- curAddr = (long *) ((Byte*) curAddr + 1); /* Update the pointer */ \
- if (++xc == Width) { /* Update the x-coordinate */ \
- xc = 0; /* If it overflows, update the y-coordinate */ \
- if (!Interlaced) /* In a non-interlaced picture, just */ \
- yc++; /* increment yc to the next scan line */ \
- else { \
- switch (Pass) { /* Otherwise deal with the interlace as */ \
- case 0: /* described in the GIF spec */ \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 4; } \
- break; \
- case 1: \
- yc += 8; \
- if (yc >= Height) { Pass++; yc = 2; } \
- break; \
- case 2: \
- yc += 4; \
- if (yc >= Height) { Pass++; yc = 1; } \
- break; \
- case 3: \
- yc += 2; \
- break; \
- default: \
- break; \
- } \
- } \
- curAddr = (long*) ((long) srcBaseAddr + (long) yc * (long) rowBytes); /* update current address in bitmap */ \
- } \
- }
- #endif
-
- #define ReadColorMap { \
- Boolean mapPresent; \
- CTabPtr tablePtr; \
- short i; \
- \
- mapPresent = (flags & ColorMapMask) ? true : false; /* Does the file have a global color map ? */ \
- if (mapPresent) { \
- BitsPerPixel = (flags & 7) + 1; /* Image bit depth */ \
- ColorMapSize = 1 << BitsPerPixel; /* Number of entries in color table */ \
- BitMask = ColorMapSize - 1; \
- \
- if (ColorTable != NULL) /* The local table overrides the global one */ \
- DisposeHandle ((Handle) ColorTable); /* so let's get rid of the latter if it exists */ \
- ColorTable = (CTabHandle) NewHandle (8*ColorMapSize+8); /* Allocate memory for the table */ \
- if (ColorTable == NULL) { e = memFullErr; abort(strGIFNoMem) } \
- HLock((Handle) ColorTable); \
- tablePtr = *ColorTable; \
- tablePtr->ctSeed = 0; /* I don't know what to put in there */ \
- tablePtr->ctFlags = 0; /* so I just zero these fields */ \
- tablePtr->ctSize = ColorMapSize - 1; \
- \
- for (i = 0; i < ColorMapSize; i++) { \
- tablePtr->ctTable[i].value = i; \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.red = (short) b * 0x100; /* Determine RGB value */ \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.green = (short) b * 0x100; \
- NextByte(b); \
- tablePtr->ctTable[i].rgb.blue = (short) b * 0x100; \
- } \
- HUnlock((Handle) ColorTable); \
- } \
- HasColorMap = HasColorMap || mapPresent; \
- }
-
-
- /************************************************************************
- ************************************************************************/
-
- OSErr ParseGIF(FSSpec *spec,GWorldPtr *world)
- {
- OSErr e; /* Error code */
- short refNum; /* File reference number */
- long sig;
- long count,FileSize;
- Byte b, flags, c;
- short BitsPerPixel, ColorMapSize, BitMask;
- short Width, Height;
- short ClearCode, FreeCode, EOFCode, FirstFree;
- Byte CodeSize, InitCodeSize;
- short MaxCode;
- Byte *BufferBase = NULL; /* Read buffer */
- Byte *BufferPointer, *BufferTop; /* Offset in buffer */
- Byte BlockCount; /* Remaining bytes in current block */
- Byte BitOffset, newBitOffset;
- long data;
- short ReadMask; /* Mask with exactly CodeSize bits set to 1 */
- short CurCode, InCode, OldCode, Code; /* Decompressor variables */
- short FinChar;
- short *OutCodeBase = NULL; /* Output array used by the decompressor */
- short *OutCode; /* Current pointer into this array */
- short *Prefix = NULL; /* The hash table used by the decompressor */
- short *Suffix = NULL;
- short xc, yc; /* Pen position */
- short Pass; /* Used by the output routines for interlaced pics */
- Boolean HasColorMap, Interlaced;
- PixMapHandle srcPixMap; /* These are associated with 'world' */
- long *srcBaseAddr, *curAddr; /* Pixmap base address and current address */
- short rowBytes;
- Rect bounds;
- CTabHandle ColorTable = NULL; /* Custom color table */
- GWorldPtr oldWorld; /* Some temporary variables */
- GDHandle oldDevice;
- long progRefNum = 0; // progress bar ref number
-
-
- if (e = FSpOpenDF (spec, fsRdPerm, &refNum)) /* Open the file */
- abort(strCantOpenData)
-
- if (!(OutCodeBase = (short*) NewPtr(2050))) { e = MemError(); abort(strGIFNoMem) }
- if (!(Prefix = (short*) NewPtr(8192))) { e = MemError(); abort(strGIFNoMem) }
- if (!(Suffix = (short*) NewPtr(8192))) { e = MemError(); abort(strGIFNoMem) }
- if (!(BufferBase = (Byte*) NewPtr(BufferSize))) { e = MemError(); abort(strGIFNoMem) }
-
- // get the file size
-
- e = GetEOF(refNum,&FileSize);
-
- Read (4, &sig); /* Read the GIF signature */
- if (sig != 'GIF8') { e = paramErr; abort(strInvalidGIFSig) } /* and make sure it's correct */
- Skip(6); /* Skip screen dimensions */
- NextByte(flags); /* This flag byte is processed below */
- Skip(2); /* Skip background color and aspect ratio */
-
- ReadColorMap /* Read the global color map */
-
- while (TRUE)
- {
- NextByte(b); /* Read the first byte of the block */
-
- switch (b)
- {
- case ImageSeparator: /* Image separator, let's go read the image */
- goto ReadImage;
- case ExtensionIntroducer: /* Extension block */
- Skip(1); /* Skip the extension label */
- NextByte(b); /* Get the block size */
- Skip(b); /* Skip the block */
-
- NextByte(b); /* Read the subsequent data blocks */
- while (b != NullBlock)
- { /* until a trailer block is reached */
- Skip(b); /* Skip this block */
- NextByte(b); /* Get length of the next block */
- }
-
- break;
- default: /* Unknown file format */
- e = paramErr;
- abort(strUnknownGIFBlock);
- }
- }
-
- ReadImage:
- Skip(4); /* Skip left and top offsets */
- NextByte(b); NextByte(c); /* Read width and height */
- Width = (short) c * 0x100 + b; /* Byte per byte because they are written */
- NextByte(b); NextByte(c); /* in stupid PC-like big-endian order */
- Height = (short) c * 0x100 + b;
-
- // $$ GCOX- Add a progress bar. We use the height of the image as the max value and advance it every
- // scan line
-
- #ifdef USE_PROGRESS_DIALOG
- e = PutUpProgressDialog(&progRefNum,Height,NULL,kCancelType);
- ProgressMessage(progRefNum,"\pOpening GIF™ File...");
- SetProgressDelay(progRefNum,120); // don't display for first 2 seconds
- #endif
- NextByte(flags);
- Interlaced = (flags & InterlaceMask) ? true : false; /* See if the image is interlaced */
-
- ReadColorMap /* If there is a local color map, read it */
-
- if (!HasColorMap) { e = paramErr; abort(strNoColorMap) } /* Make sure we have at least one color map */
-
- GetGWorld(&oldWorld, &oldDevice); /* save the current world */
- SetRect(&bounds, 0, 0, Width, Height); /* and create our own */
-
- (*ColorTable)->ctSeed = GetCTSeed(); // $$GPC- set some seed value rather than zero (in 7.5.3+,
- // failure to do this will hang the system)
-
- if ( e = MakeSafeGWorld( world, &bounds, 8, ColorTable, TRUE) )
- abort( strGIFNoMem )
-
- LockPixels(GetGWorldPixMap(*world)); /* Lock the pixel map */
- SetGWorld (*world, NULL); /* activate it */
-
- NextByte(CodeSize);
- ClearCode = (1 << CodeSize);
- EOFCode = ClearCode + 1;
- FreeCode = FirstFree = ClearCode + 2;
-
- CodeSize++;
- InitCodeSize = CodeSize;
- MaxCode = (1 << CodeSize);
- ReadMask = MaxCode - 1;
-
- BlockCount = 0; /* We start at the beginning of a block */
- BitOffset = 0; /* and at the beginning of a byte */
- BufferPointer = BufferTop = BufferBase + BufferSize; /* Force the buffer to be filled immediately */
-
- xc = yc = 0;
- Pass = 0;
-
- srcPixMap = GetGWorldPixMap(*world); /* Get the pixmap */
- rowBytes = (*srcPixMap)->rowBytes & 0x7FFF; /* get rowBytes */
- curAddr = srcBaseAddr = (long*) GetPixBaseAddr (srcPixMap); /* and the base address of the pixmap */
-
- ReadCode(Code);
- while (Code != EOFCode)
- {
- if (Code == ClearCode)
- { /* Clear code sets everything back to its */
- CodeSize = InitCodeSize; /* initial value, then reads the subsequent code */
- MaxCode = (1 << CodeSize); /* as uncompressed data. */
- ReadMask = MaxCode - 1;
- FreeCode = FirstFree;
- ReadCode(Code);
- CurCode = OldCode = Code;
- FinChar = CurCode & BitMask;
- DrawPixel(FinChar);
- }
- else
- { /* If not Clear code, then must be data. */
- CurCode = InCode = Code; /* Save same as CurCode and InCode */
-
- OutCode = OutCodeBase;
- if (CurCode >= FreeCode)
- { /* If >= FreeCode, not in the hash table yet */
- CurCode = OldCode; /* repeat the last character decoded */
- *(OutCode++) = FinChar;
- }
-
- while (CurCode > BitMask)
- { /* Pursue the chain pointed to by CurCode */
- *(OutCode++) = Suffix[CurCode]; /* through the hash table to its end */
- CurCode = Prefix[CurCode]; /* the output queue */
- }
-
- FinChar = CurCode & BitMask; /* The last code in the chain is treated as raw data. */
- *OutCode = FinChar;
-
- while (OutCode >= OutCodeBase) /* Now we put the data out */
- DrawPixel(*(OutCode--)); /* It's been stacked LIFO, so deal with it that way */
-
- Prefix[FreeCode] = OldCode; /* Build the hash table on-the-fly */
- Suffix[FreeCode] = FinChar; /* No table is stored in the file */
- OldCode = InCode;
-
- if (++FreeCode >= MaxCode) /* Point to the next slot in the table */
- if (CodeSize < 12)
- { /* If we exceed the current MaxCode value */
- CodeSize++; /* increment the code size unless it's already 12 */
- MaxCode *= 2; /* If it is, do nothing; the next code better be */
- ReadMask = MaxCode - 1; /* Clear */
- }
- }
-
- ReadCode(Code); /* Read the next code */
- }
-
- UnlockPixels(GetGWorldPixMap(*world)); /* Unlock the pixel map */
- SetGWorld (oldWorld, oldDevice); /* restore the previous graphics world */
-
- e = noErr;
- end:
- #ifdef USE_PROGRESS_DIALOG
- if (progRefNum)
- TakeDownProgressDialog(progRefNum);
- #endif
- if (ColorTable) /* We can destroy the CTable, NewGWorld */
- DisposeHandle((Handle) ColorTable);
-
- if (BufferBase)
- DisposePtr((Ptr) BufferBase);
-
- if (Suffix)
- DisposePtr((Ptr) Suffix);
-
- if (Prefix)
- DisposePtr((Ptr) Prefix);
-
- if (OutCodeBase)
- DisposePtr((Ptr) OutCodeBase);
-
- (void) FSClose(refNum);
-
- return(e);
- }
-
-
- //*************************************************************************************************************************
-
-
- // This is the mid-level function to open GIF files, called from the main file dispatcher
-
- #ifndef __COLOURLAB__
-
-
- OSErr LLOpenGIFFile(FSSpec *fileSpec,WindowPtr theWindow)
- {
- OSErr theErr;
- GWorldPtr tempWorld = NULL,theImage;
- t3DWindowHdl t3DH;
- Boolean needNewWindow;
- Rect dummy,iFrame;
- short resRefNum;
-
- needNewWindow = (theWindow == NULL);
-
- theErr = ParseGIF(fileSpec,&tempWorld);
-
- if (theErr == noErr)
- {
- // we actually got the bloody thing! Now either open a new window or install the
- // GWorld into the existing one. Open the resource fork so we can take advantage
- // of any special window positioning resource
-
- if (needNewWindow)
- {
- resRefNum = FSpOpenResFile(fileSpec,fsCurPerm);
- if (resRefNum != -1)
- UseResFile(resRefNum);
-
- SetRect(&dummy,0,0,0,0);
- theWindow = GetNew3DWindow(kSOWindowID,&dummy,-1,GetWindowInFront(userKind));
-
- // this creates a window without a GWorld (-1 passed as depth parameter)
-
- if (resRefNum != -1)
- CloseResFile(resRefNum);
- }
-
- t3DH = GetWindowInfo(theWindow);
-
- if (t3DH)
- {
- theImage = GetMasterLayerImage(t3DH);
-
- if(!needNewWindow && theImage)
- DisposeGWorld(theImage); // needed for revert, etc
-
- iFrame = (*GetGWorldPixMap(tempWorld))->bounds;
- SetMasterLayerImage(t3DH,tempWorld);
-
- CalibrateScrollBars(t3DH);
- SetImageMagnification(t3DH,100);
- InstallWindowPalette(theWindow);
-
- SetWTitle(theWindow,fileSpec->name);
- ShowFWWindow(theWindow);
- SelectFWWindow(theWindow);
- if (needNewWindow)
- RegisterNewWindow(gWMList,theWindow);
- (*t3DH)->imageCameFromFile = TRUE;
- (*t3DH)->imageFile = *fileSpec;
- (*t3DH)->imageToBeSaved = FALSE;
- (*t3DH)->imageType = 'GIFf';
- }
- else
- {
- theErr = -108;
- DisposeGWorld(tempWorld);
- }
- }
- return(theErr);
- }
-
-
- #endif
-
-
-
- OSErr ConvertGIFtoPICT(FSSpec *theFile,PicHandle *thePicture)
- {
- // converts an open GIF file to a picture- used to create a preview image for the file, amongst other
- // possible uses
-
- OSErr theErr;
- GWorldPtr tempWorld;
- CGrafPtr savePort;
- GDHandle saveDevice;
- PixMapHandle gifPixels;
- PicHandle thePic;
- Rect picRect;
-
- theErr = ParseGIF(theFile,&tempWorld);
-
- if (theErr == noErr)
- {
- GetGWorld(&savePort,&saveDevice);
- SetGWorld(tempWorld,NULL);
-
- gifPixels = GetGWorldPixMap(tempWorld);
- picRect = (*gifPixels)->bounds;
-
- thePic = OpenPicture(&picRect);
- ClipRect(&picRect);
- if (LockPixels(gifPixels))
- {
- CopyBits((BitMap*)*gifPixels,(BitMap*)*gifPixels,&picRect,&picRect,srcCopy,NULL);
- UnlockPixels(gifPixels);
- }
- ClosePicture();
-
- SetGWorld(savePort,saveDevice);
- DisposeGWorld(tempWorld);
-
- picRect = (*thePic)->picFrame;
- if (EmptyRect(&picRect))
- theErr = memFullErr; // picture creation failed
-
- *thePicture = thePic;
- }
- return(theErr);
- }